from django import forms
from web import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.conf import settings
from utils import sms
import random
from django_redis import get_redis_connection
from utils import md5
from .bootstrap import BootStrapFrom


class RegisterModeForm(BootStrapFrom, forms.ModelForm):
    # 重写原字段属性,如果没有这个字段则创建新的，如果有则覆盖

    mobile_phone = forms.CharField(label="手机号码", max_length=11,
                                   # 匹配手机号格式
                                   validators=[RegexValidator(r'^(1[[|3|4|5|6|7|8|9|])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(label='密码',
                               max_length=32,
                               min_length=8,
                               error_messages={
                                   'min_length': '最少为8位',
                                   'max_length': '最大为32位'
                               },
                               widget=forms.PasswordInput)  # 改为密文输入
    re_password = forms.CharField(label='确认密码', max_length=32, widget=forms.PasswordInput)
    code = forms.CharField(label='验证码')

    class Meta:
        model = models.UserInfo
        # 按照自己需求顺序展示字段
        fields = ['username', 'email', 'password', 're_password', 'mobile_phone', 'code']

    # 初始化参数，为每个字段都添加form 类属性
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            # name : 数据库的字段名
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % field.label

    # 校验用户是否存在
    def clean_username(self):
        # 获取 注册是用户名
        username = self.cleaned_data.get('username')
        # 和数据库中的用户名做对比
        user_obj = models.UserInfo.objects.filter(username=username).first()
        if user_obj:
            # 如果用户存在 则返回错误信息
            raise ValidationError('该用户已存在')
        return username

    # 校验手机号是否唯一
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')
        # if len(mobile_phone) != 11:
        #     raise ValidationError('手机号码输入有误,且必须为11位')
        phone_obj = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if phone_obj:
            raise ValidationError('该手机号已被注册')
        return mobile_phone

    # 校验两次密码是否一致
    # def clean(self):
    #     password = self.cleaned_data.get('password')
    #     re_password = self.cleaned_data.get('re_password')
    #     if password == re_password:
    #         return self.cleaned_data
    #     raise ValidationError('两次密码不一致')

    def clean_password(self):
        # 对密码进行加密
        pwd = self.cleaned_data.get('password')
        md5_pwd = md5.str_to_md5(pwd)
        return md5_pwd

    def clean_re_password(self):
        # 校验两次密码是否一致
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd != md5.str_to_md5(re_pwd):
            raise ValidationError('两次密码不一致')
        return re_pwd

    # 校验 验证码输入是否正确
    def clean_code(self):
        # 获取用户输入的验证码
        code = self.cleaned_data.get('code')
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        # 从redis 中获取验证码
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        # 判断 数据库里的验证码是否存在
        if not redis_code:
            raise ValidationError('验证码失效或未发送')
        # 判断 输入验证码与redis里的是否一致
        redis_str_code = redis_code.decode('utf-8')
        # print(redis_str_code,type(redis_str_code))
        # print(code)
        if str(code) != redis_str_code:
            raise ValidationError('验证码输入有误')
        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label="手机号码", max_length=11,
                                   # 匹配手机号格式
                                   validators=[RegexValidator(r'^(1[[|3|4|5|6|7|8|9|])\d{9}$', '手机号格式错误'), ])

    # 重写父类方法，接收带有模板类型的数据，进行判断
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """手机号码校验钩子"""
        mobile_phone = self.cleaned_data['mobile_phone']
        # 判断发送手机验证的短信模板是否正确
        template_type = self.request.GET.get('type')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(template_type)
        if not template_id:
            raise ValidationError('短信模板错误')
        # 判断手机号是否已经注册
        exist = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        # 获取模板类型
        if template_type == 'login':
            if not exist:
                raise ValidationError('该手机号还未注册账号')
        else:
            if exist:
                raise ValidationError('该手机号码已经被注册')
        # 发短信
        code = random.randrange(1000, 9999)
        res = sms.send_sms_single(mobile_phone, template_id, [code])
        if res['result'] != 0:
            raise ValidationError('短信发送失败:%s' % res['errmsg'])

        # 手机号、验证码 写入redis (django-redis组件)
        # 去连接池中获取一个连接
        conn = get_redis_connection()
        conn.set(mobile_phone, code, ex=60)  # 设置key：val 存活时间60秒
        return mobile_phone


# 用户短信登录form

class LoginSmsForm(BootStrapFrom, forms.Form):
    mobile_phone = forms.CharField(label="手机号码", max_length=11,
                                   # 匹配手机号格式
                                   validators=[RegexValidator(r'^(1[[|3|4|5|6|7|8|9|])\d{9}$', '手机号格式错误'), ])
    code = forms.CharField(label='验证码')

    def clean_mobile_phone(self):
        # 对手机号进行校验
        mobile_phone = self.cleaned_data.get('mobile_phone')
        mobile_phone_obj = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not mobile_phone_obj:
            raise ValidationError('该手机号还未注册')
        return mobile_phone

    def clean_code(self):
        # 验证 验证码
        code = self.cleaned_data.get('code')
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code  # 如果手机号不存在 则无需验证验证码
        # 连接redis 获取验证码
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送')
        str_redis_code = redis_code.decode('utf-8')
        if code.strip() != str_redis_code:
            raise ValidationError('验证码输入有误')
        return code


# 用户登录

class LoginForm(BootStrapFrom, forms.Form):
    username = forms.CharField(label='手机号/邮箱', max_length=32)
    password = forms.CharField(label='密码', widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='图形验证码')

    # 对密码进行验证，重写父类init方法，接收request
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    # 对密码进行MD5加密
    def clean_password(self):
        pwd = self.cleaned_data.get('password')
        pwd_md5 = md5.str_to_md5(pwd)
        return pwd_md5

    # 对验证码进行校验，和session中的对比

    def clean_code(self):
        code = self.cleaned_data.get('code')
        session_code = self.request.session.get('image_code')
        # print(code.strip().upper(), session_code.strip().upper())
        if not session_code:
            raise ValidationError('验证码不存在或已过期')
        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码输入有误')
        return code


class SetPwdForm(BootStrapFrom,forms.Form):
    old_password = forms.CharField(label='旧密码',widget=forms.PasswordInput(render_value=True) )
    password = forms.CharField(label='新密码', max_length=32,
                               min_length=8,
                               error_messages={
                                   'min_length': '最少为8位',
                                   'max_length': '最大为32位'},
                               widget=forms.PasswordInput(render_value=True))
    re_password = forms.CharField(label='确认新密码',widget=forms.PasswordInput(render_value=True) )

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_old_password(self):
        old_password = self.cleaned_data.get('old_password')
        md5_old_pwd = md5.str_to_md5(old_password)
        user_obj = models.UserInfo.objects.filter(id=self.request.tracer.user.id, password=md5_old_pwd).first()
        if not user_obj:
             raise ValidationError('旧密码错误')
        return old_password

    def clean_password(self):
        md5_pwd = md5.str_to_md5(self.cleaned_data.get('password'))
        return md5_pwd

    def clean_re_password(self):
        re_pwd = self.cleaned_data.get('re_password')
        md5_re_pwd = md5.str_to_md5(re_pwd)
        pwd = self.cleaned_data.get('password')
        if md5_re_pwd != pwd:
            raise ValidationError('两次密码不一致')
        return md5_re_pwd